home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d20 / msgq160s.arc / MSGLINK.C < prev    next >
Text File  |  1991-10-26  |  15KB  |  594 lines

  1. /*
  2.  * MSGLINK.C - Link QuickBBS reply chains using EID lines
  3.  *
  4.  * Msged/Q message editor for QuickBBS  Copyright 1990 by P.J. Muller
  5.  *
  6.  * This file is not used by Msged/Q
  7.  */
  8.  
  9. #define SHOWMEM
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #ifdef SHOWMEM
  15. #include <alloc.h>
  16. #endif
  17.  
  18. #define MAIN
  19. #include "msglink.h"
  20.  
  21. typedef BYTE BITSET[32];        /* [0..255] */
  22.  
  23. #define clearset(a)    memset(a,0,sizeof(BITSET));
  24. #define setbit(b,a)    a[(b)/8] |= (1 << ((b) % 8))
  25. #define isset(b,a)    (a[(b)/8] & (1 << ((b) % 8)))
  26.  
  27. typedef struct {
  28.     int num;            /* board number */
  29.     char *name;            /* board name */
  30. } BOARD;
  31.  
  32. static BOOLEAN readeid(MSGHEADER *hdr, ID *EID, ID *RID);
  33. static int readlinks(BITSET boards, MSGDATA ***link);
  34. #ifdef DEBUG
  35. static void showlist(MSGDATA **link, int len);
  36. #endif
  37. static void linklist(MSGDATA **link, int LEN);
  38. static int sort_st_crc(MSGDATA **a, MSGDATA **b);
  39. static char *prepsubj(char *s);
  40. static void dosubjs(MSGDATA **link, int len);
  41. static int sort_num(MSGDATA **a, MSGDATA **b);
  42. static void writelinks(MSGDATA **link,int len);
  43. static void parseargs(int argc,char *argv[],BITSET useboard,BOOLEAN *subj,BOOLEAN *eid);
  44. static BOARD **readareas(char *fname);
  45. static BOOLEAN readtosslog(char *tosslog, BOARD **board, BITSET useboard);
  46.  
  47. static BOOLEAN subjlink;        /* link Subj lines */
  48. static BOOLEAN eidlink;            /* link EID lines */
  49.  
  50. #define fatal(s)    { fprintf(stderr,"\nFatal: %s\n",s);  exit(1); }
  51.  
  52. int main(int argc, char *argv[])
  53. {
  54.   MSGDATA **link;
  55.   int len;
  56. #ifdef SHOWMEM
  57.   long memory;
  58. #endif
  59.   BITSET useboard;
  60.  
  61.   fprintf(stderr,"EID message linking  Version 1.1  Copyright 1991  P.J. Muller 5:7102/11\n\n");
  62.  
  63.   parseargs(argc,argv,useboard,&subjlink,&eidlink);
  64.  
  65.   if (!openmsgbase())
  66.     fatal("Could not open message base");
  67.  
  68. #ifdef SHOWMEM
  69.   memory = coreleft();
  70. #endif
  71.   len = readlinks(useboard, &link);
  72.   if (len != 0) {
  73. #ifdef SHOWMEM
  74.     fprintf(stderr,"\n\tUsing %dk memory",(int)((memory-coreleft())/1024));
  75. #endif
  76.  
  77.     fprintf(stderr,"\n\nLinking messages...");
  78.     if (subjlink)
  79.       dosubjs(link,len);
  80.  
  81.     linklist(link,len);        /* after this link will be sortde by number */
  82.  
  83.     fprintf(stderr,"\n\nWriting new links...");
  84.     writelinks(link,len);
  85.     fprintf(stderr,"\n\tDone\n");
  86.   } else {
  87.     fprintf(stderr,"Less than two messages selected -> nothing to do!\n");
  88.   } /* else */
  89.  
  90.   if (!closemsgbase())
  91.     fatal("Could not close message base");
  92.  
  93.   return(0);
  94. } /* main */
  95.  
  96. /*
  97.  * Compare function for sort by time/date stamp then CRC then board number
  98.  */
  99.  
  100. static int sort_st_crc(MSGDATA **a, MSGDATA **b)
  101. {
  102.   int diff;
  103.  
  104.   if ((*a)->EID.st < (*b)->EID.st)
  105.     return(-1);                /* a < b */
  106.   if ((*a)->EID.st > (*b)->EID.st)
  107.     return(1);                /* a > b */
  108.  
  109. /* time/date stamps are equal */
  110.  
  111.             /* can do this trick because crc is an int */
  112.   diff = (*a)->EID.crc - (*b)->EID.crc;
  113.   if (diff != 0)
  114.     return(diff);
  115.             /* cast to int for trick */
  116.   return((int)(*a)->area - (int)(*b)->area);
  117. } /* sort_st_crc */
  118.  
  119. /*
  120.  * Compare function for sort by area then subj then number
  121.  */
  122.  
  123. static int sort_subj(MSGDATA **a, MSGDATA **b)
  124. {
  125.   int diff;
  126.  
  127.   diff = (int)(*a)->area - (int)(*b)->area;
  128.   if (diff != 0)
  129.     return(diff);
  130.  
  131.   diff = strcmp((*a)->subj,(*b)->subj);
  132.   if (diff != 0)
  133.     return(diff);
  134.  
  135.   return((*a)->no - (*b)->no);
  136. #ifdef ZAPTHISOUT
  137.   if ((*a)->EID.st < (*b)->EID.st)
  138.     return(-1);                /* a < b */
  139.   if ((*a)->EID.st > (*b)->EID.st)
  140.     return(1);                /* a > b */
  141.  
  142.   return(0);
  143. #endif
  144. } /* sort_subj */
  145.  
  146. /*
  147.  * Compare function for sort by 'no'
  148.  */
  149.  
  150. static int sort_num(MSGDATA **a, MSGDATA **b)
  151. {
  152.   return((*a)->no - (*b)->no);
  153. } /* sort_num */
  154.  
  155. /*
  156.  * Display the list of messages
  157.  */
  158.  
  159. #ifdef DEBUG
  160. static void showlist(MSGDATA **link, int len)
  161. {
  162.   printf("\n");
  163.   while (len--) {
  164.     printf("%4d %04x %08lx %04x %08lx %4d %4d %4d %4d %s\n", (*link)->no,
  165.         (*link)->EID.crc,(*link)->EID.st,
  166.         (*link)->x.RID.crc,(*link)->x.RID.st,
  167.         (*link)->up,(*link)->down,
  168.         (*link)->oup,(*link)->odown,
  169.         (*link)->subj == NULL ? "" : (*link)->subj);
  170.     link++;
  171.   } /* while */
  172.   printf("\n");
  173. } /* showlist */
  174. #endif
  175.  
  176. /*
  177.  * Link the messages
  178.  */
  179.  
  180. static void linklist(MSGDATA **numidx, int LEN)
  181. {
  182.   int c;
  183.   size_t len = LEN;        /* lfind wants a pointer to size_t */
  184.   MSGDATA key,*keyptr,**dateidx;
  185.   MSGDATA **found;
  186.  
  187.   fprintf(stderr,"\n\tSorting EID's");
  188.  
  189.   dateidx = calloc(len,sizeof(MSGDATA *));    /* make another index */
  190.   if (dateidx == NULL)
  191.     fatal("Out of memory.");
  192.   memcpy(dateidx,numidx,len*sizeof(MSGDATA *));
  193.   qsort(dateidx,len,sizeof(MSGDATA *),sort_st_crc);
  194.   qsort(numidx,len,sizeof(MSGDATA *),sort_num);
  195. #ifdef DEBUG
  196.   /*showlist(numidx,len);*/
  197. #endif
  198.  
  199.   fprintf(stderr,"\n\tLinking EID's");
  200.   memset(&key,0,sizeof(key));
  201.   keyptr = &key;
  202.   for (c = 0;  c < len;  c++) {
  203.     if (numidx[c]->x.RID.st == 0)
  204.       continue;                /* no RID */
  205.     key.area = numidx[c]->area;
  206.     key.EID = numidx[c]->x.RID;
  207.     found = bsearch(&keyptr,dateidx,len,sizeof(dateidx[0]),sort_st_crc);
  208.                 /* step to end of reply list */
  209.     while ((found != NULL) && (key.no = (*found)->up) != 0)
  210.       found = bsearch(&keyptr,numidx,len,sizeof(numidx[0]),sort_num);
  211.  
  212.     if (found != NULL) {        /* add link */
  213.       if ((*found)->no < numidx[c]->no) {    /* avoid endless loops */
  214.     (*found)->up = numidx[c]->no;
  215.     numidx[c]->down = (*found)->no;
  216.       } /* if */
  217.     } /* if */
  218.  
  219.   } /* for */
  220.  
  221. #ifdef DEBUG
  222.   /* showlist(numidx,len); */
  223. #endif
  224.  
  225. } /* linklist */
  226.  
  227. /*
  228.  * Link the Subj lines
  229.  */
  230.  
  231. static void dosubjs(MSGDATA **link, int len)
  232. {
  233.   MSGDATA **step, **base=link;
  234.   int c;
  235.  
  236.   fprintf(stderr,"\n\tSorting Subjects");
  237.   qsort(link,len,sizeof(link[0]),sort_subj);
  238.  
  239.   fprintf(stderr,"\n\tLinking Subjects");
  240.   c = len;
  241.   while (c > 1) {
  242.     step = base+1;        /* Subj after current */
  243.     if ((*base)->EID.st != 0)    /* skip empty EID's */
  244.       while ((--c > 0) && ((*base)->area == (*step)->area) &&
  245.          (strcmp((*base)->subj,(*step)->subj)) == 0) {
  246.     (*step)->x.RID = (*base)->EID;    /* create RID */
  247.     ++step;
  248.       } /* while */
  249.     base = step;        /* skip same subj's */
  250.   } /* while */
  251. #ifdef DEBUG
  252.   /* showlist(link,len); */
  253. #endif
  254.  
  255. #ifndef DEBUG            /* free()'ing not really necessary */
  256.   for (c = 0, step = link;  c < len;  c++, step++) {
  257.     free((*step)->subj);
  258.     (*step)->subj = NULL;
  259.   } /* for */
  260. #endif
  261.  
  262. } /* dosubjs */
  263.  
  264. /*
  265.  * Read all the links of a message base
  266.  * Return an array of pointers to MSGDATA instances
  267.  */
  268.  
  269. static int readlinks(BITSET boards, MSGDATA ***link)
  270. {
  271.   MSGHEADER header;
  272.   MSGDATA *temp;
  273.   int board, idx, len, maxlen;
  274.   ID EID,RID;
  275.  
  276.   maxlen = 0;
  277.   for (idx=0;  idx < filemsgs;  idx++) {
  278.     if ((msgidx[idx].msgnum == -1) || (msgidx[idx].board == 0))
  279.       continue;                /* deleted message */
  280.  
  281.     board = msgidx[idx].board;        /* board number */
  282.     if (!isset(board-1,boards))
  283.       continue;                /* we are not interested */
  284.  
  285.     maxlen++;                /* one more */
  286.   } /* for */
  287.  
  288.   if (maxlen < 2) return 0;
  289.  
  290.   fprintf(stderr,"Reading %d messages...",maxlen);
  291.  
  292.   *link = calloc(maxlen,sizeof(MSGDATA *));    /* allocate array */
  293.   if (*link == NULL)
  294.     fatal("Out of memory.");
  295.  
  296. /*
  297.  * Now read the message headers
  298.  */
  299.  
  300.   fprintf(stderr,"\n\tReading Headers");
  301.   len = 0;        /* will now count real length */
  302.   for (idx=0;  idx < filemsgs;  idx++) {
  303.     if ((msgidx[idx].msgnum == -1) || (msgidx[idx].board == 0))
  304.       continue;                /* deleted message */
  305.  
  306.     board = msgidx[idx].board;        /* board number */
  307.     if (!isset(board-1,boards))
  308.       continue;                /* we are not interested */
  309.  
  310.     if (readheader(msgidx[idx].msgnum,&header)) {
  311.       if (header.numrecs == 0) continue;
  312.  
  313.       if ((temp = calloc(1,sizeof(MSGDATA))) == NULL)
  314.     fatal("Out of memory.");
  315.  
  316.       temp->no = header.msgnum;
  317.       temp->area = board;
  318.       temp->oup = header.up;
  319.       temp->odown = header.reply;
  320.       temp->EID.st = dostime(header.posttime,header.postdate,TRUE);
  321.       temp->EID.crc = rand();
  322.       temp->x.startrec = header.startrec;
  323.       if (subjlink)
  324.     temp->subj = prepsubj(header.subj);
  325.  
  326.       (*link)[len++] = temp;    /* pointer to last */
  327.     } /* if */
  328.  
  329.   } /* for */
  330.  
  331. /*
  332.  * By this time all messages will have a fake EID in memory
  333.  * Now start reading real EID's and reply EID's if required
  334.  */
  335.  
  336.   if (eidlink) {
  337.     fprintf(stderr,"\n\tReading EID's");
  338.     memset(&header,0,sizeof(header));
  339.     for (idx=0;  idx < len;  idx++) {
  340.       temp = (*link)[idx];
  341.       header.numrecs = 1;            /* read only 255 chars */
  342.       header.startrec = temp->x.startrec;    /* knows about the insides */
  343.       header.msgnum = temp->no;            /*  of readtext() */
  344.       if (!readeid(&header,&EID,&RID))
  345.     fatal("Error while reading message text (memory?)");
  346.       if (EID.st != 0) {            /* found and EID, override default */
  347.     temp->EID = EID;
  348.     temp->x.RID = RID;
  349.       } /* if */
  350.     } /* for */
  351.   } /* if */
  352.  
  353.   return(len);
  354. } /* readlinks */
  355.  
  356. static BOOLEAN readeid(MSGHEADER *hdr, ID *EID, ID *RID)
  357. {
  358.   char *text,*eid;
  359.   int count;
  360.  
  361.   memset(EID,0,sizeof(ID));
  362.   memset(RID,0,sizeof(ID));
  363.  
  364.   if ((text = readtext(hdr)) == NULL)
  365.     return(FALSE);
  366.  
  367.   eid = strstr(text,"\01EID:");
  368.   if (eid != NULL)
  369.     count = sscanf(eid, "\01EID: %x %X %x %X\r", &EID->crc, &EID->st, &RID->crc, &RID->st);
  370.  
  371.   switch (count) {        /* check for partial match */
  372.     case 1:
  373.       memset(EID,0,sizeof(ID));
  374.       break;
  375.     case 3:
  376.       memset(RID,0,sizeof(ID));
  377.       break;
  378.   } /* switch */
  379.  
  380.   free(text);
  381.  
  382.   return(TRUE);
  383. } /* readeid */
  384.  
  385. /*
  386.  * Write all the new links of a message base
  387.  */
  388.  
  389. static void writelinks(MSGDATA **link,int len)
  390. {
  391.   MSGHEADER header;
  392.   MSGDATA **step;
  393.   int c;
  394.  
  395. #ifdef DEBUG
  396.   /* showlist(link,len); */
  397. #endif
  398.  
  399.   fprintf(stderr,"\n\tWriting links");
  400.   for (c = 0, step = link;  c < len;  c++, step++) {
  401.     if (((*step)->up == (*step)->oup) && ((*step)->down == (*step)->odown))
  402.       continue;        /* no change to links */
  403.     if (!readheader((*step)->no,&header))
  404.       continue;        /* can't read */
  405.     header.up = (*step)->up;
  406.     header.reply = (*step)->down;
  407.     writeheader(&header);
  408.   } /* for */
  409.  
  410. } /* writelinks */
  411.  
  412. /*
  413.  * Prepare a Subj line and return a malloc'ed string
  414.  */
  415.  
  416. static char *prepsubj(char *s)
  417. {
  418.   char subj[73];
  419.  
  420.   while (strnicmp(s,"Re: ",4) == 0)
  421.     s += 4;        /* skip leading RE:'s */
  422.  
  423.   strcpy(subj,s);
  424.   subj[25] = EOS;    /* at most 25 characters */
  425.   strupr(subj);
  426.  
  427.   return(strdup(subj));
  428. } /* prepsubj */
  429.  
  430. /*
  431.  * Parse the command line
  432.  */
  433.  
  434. static void parseargs(int argc, char *argv[], BITSET useboard, BOOLEAN *subj, BOOLEAN *eid)
  435. {
  436.   char *toss = NULL;
  437.   BOOLEAN dirty = FALSE;
  438.  
  439.   clearset(useboard);
  440.  
  441.   if (argc < 2) {
  442.     fprintf(stderr,"Usage: MsgLink [-s] [-e] [-llogname] [board] [board1-board2] ...\n");
  443.     fprintf(stderr,"\nLinks QuickBBS messages in numerical order using ^aEID and Subject lines.\n");
  444.     fprintf(stderr,"\n\t-e\tDisable ^aEID kludge line linking\n");
  445.     fprintf(stderr,  "\t-s\tDisable Subject line linking\n");
  446.     fprintf(stderr,  "\t-l\tSpecify a tossed board log file\n");
  447.     fprintf(stderr,"\nE.g.:\tMsgLink 1-2 3-6 10  Links messages in boards 1-6 and 10\n");
  448.     fprintf(stderr,  "\tMsgLink -s 1-200    Links all messages using only EID's.\n");
  449.     fprintf(stderr,  "\tMsgLink -e 3 132    Links messages in board 3 & 132 using only Subj.\n");
  450.     fprintf(stderr,  "\tMsgLink -lQECHO.TMP Links messages in boards listed in QECHO.TMP\n");
  451.     exit(1);
  452.   } /* if */
  453.  
  454.   *subj = TRUE;
  455.   *eid = TRUE;
  456.  
  457.   while (--argc) {
  458.     ++argv;
  459.     if (*argv[0] == '-') {
  460.       switch (argv[0][1]) {
  461.     case 's':
  462.     case 'S':
  463.       *subj = FALSE;
  464.       break;
  465.  
  466.     case 'e':
  467.     case 'E':
  468.       *eid = FALSE;
  469.       break;
  470.  
  471.     case 'l':
  472.     case 'L':
  473.       toss = argv[0]+2;        /* skip '-L' */
  474.       if (strlen(toss) == 0)
  475.         fatal("Must specify filename directly after -L");
  476.       break;
  477.  
  478.     default:
  479.       fprintf(stderr,"Bad option '%c'\n",argv[0][1]);
  480.       exit(1);
  481.       } /* switch */
  482.     } else {
  483.       int count, a=0, b=0;
  484.       count = sscanf(argv[0],"%d-%d",&a,&b);
  485.       switch (count) {
  486.     case 1:
  487.       if ((a >= 1) && (a <= BLIM)) {
  488.         setbit(a-1,useboard);
  489.         dirty = TRUE;
  490.       } else
  491.         count = 0;
  492.       break;
  493.  
  494.     case 2:
  495.       if ((a >= 1) && (b <= BLIM)) {
  496.         for (count=a-1;  count < b;  count++)
  497.           setbit(count,useboard);
  498.         dirty = TRUE;
  499.       } else
  500.         count = 0;
  501.       break;
  502.      } /* switch */
  503.  
  504.      if (count == 0) {
  505.        fprintf(stderr,"Bad argument '%s'\n",argv[0]);
  506.        exit(1);
  507.      } /* if */
  508.    } /* else */
  509.   } /* while */
  510.  
  511. /* Now read the tosslog and areas.bbs if specified */
  512.   if (toss != NULL) {
  513.     BOARD **board,**step;
  514.     if ((board = readareas("areas.bbs")) != NULL) {
  515.       dirty |= readtosslog(toss,board,useboard);
  516.       for (step = board;  *step != NULL;  step++) {
  517.     free(step[0]->name);
  518.     free(step[0]);
  519.       } /* for */
  520.       free(board);
  521.       board = NULL;
  522.     } /* if */
  523.   } /* if */
  524.  
  525.   if ((!dirty) || !(*subj || *eid)) {
  526.     fprintf(stderr,"Nothing to do!\n");
  527.     exit(1);
  528.   } /* if */
  529. } /* parseargs */
  530.  
  531. static BOARD **readareas(char *fname)
  532. {
  533.   FILE *areas;
  534.   char buffer[101];
  535.   char bname[51];
  536.   int match,num;
  537.   BOARD **list;
  538.   int sp = 0;
  539.  
  540.   list = calloc(BLIM+1,sizeof(list[0]));
  541.  
  542.   if ((areas = fopen(fname,"r")) == NULL) {
  543.     fprintf(stderr,"Cannot open %s\n",fname);
  544.     exit(1);
  545.   } /* if */
  546.  
  547.   while (fgets(buffer,100,areas) != NULL) {
  548.     match = sscanf(buffer,"%d %50s",&num,bname);
  549.     if (match == 2) {
  550.       list[sp] = calloc(1,sizeof(*list[0]));
  551.       list[sp]->num = num;
  552.       list[sp]->name = strdup(bname);
  553.       sp++;
  554.     } /* if */
  555.   } /* while */
  556.  
  557.   list[sp] = NULL;
  558.   list = realloc(list,(sp+1)*sizeof(list[0]));
  559.  
  560.   fclose(areas);
  561.  
  562.   return(list);
  563. } /* readareas */
  564.  
  565. static BOOLEAN readtosslog(char *tosslog, BOARD **board, BITSET useboard)
  566. {
  567.   FILE *log;
  568.   BOARD **look;
  569.   char name[51];
  570.   BOOLEAN dirty = FALSE;
  571.  
  572.   if (board == NULL)
  573.     return(FALSE);
  574.  
  575.   if ((log = fopen(tosslog,"r")) == NULL) {
  576.     fprintf(stderr,"Could not open %s\n",tosslog);
  577.     exit(1);
  578.   } /* if */
  579.  
  580.   while (fscanf(log,"%50s",&name) > 0) {
  581.     for (look = board;  look[0] != NULL;  look++)
  582.       if (stricmp(look[0]->name,name) == 0)
  583.     break;
  584.     if (*look != NULL) {
  585.       setbit(look[0]->num-1,useboard);    /* select that board */
  586.       dirty = TRUE;
  587.     } /* if */
  588.   } /* while */
  589.  
  590.   fclose(log);
  591.  
  592.   return(dirty);
  593. } /* readtosslog */
  594.